Elasticsearch(十一)搜索

您所在的位置:网站首页 elasticsearch 日期范围查询 Elasticsearch(十一)搜索

Elasticsearch(十一)搜索

2024-02-22 08:39| 来源: 网络整理| 查看: 265

一、前言

继上一节学习了ES的搜索的查询全部和term搜索后,此节将把搜索匹配功能剩余的2个学习完,分别是range搜索和exists搜索

二、range范围搜索

range查询用于范围查询,一般是对数值型和日期型数据的查询。使用range进行范围查询时,用户可以按照需求中是否包含边界数值进行选项设置,可供组合的选项如下:

gt:大于;lt 小于;gte 大于等于;lte 小于等于;

其请求形式如下:

GET /hotel/_search { "query": { "range": { "FIELD": { //需要范围查询的列 "gte": "${VALUE1}", //大于等于value1 "lte": "${VALUE2}" //小于等于value2 } } } }

以下是数值类型的查询示例,查询住宿价格在500~600(包含边界值)元的酒店:

GET /hotel/_search { "query": { "range": { "price": { "gte": "500", "lte": "600" } } } }

ES返回的数据如下:

{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "hotel", "_type" : "_doc", "_id" : "001", "_score" : 1.0, "_source" : { "title" : "文雅酒店", "city" : "北京", "price" : "558.00", "create_time" : "2020-03-29 21:00:00", "amenities" : "浴池,普通停车场/充电停车场", "full_room" : true, "location" : { "lat" : 36.940243, "lon" : 120.394 }, "praise" : 10 } }, { "_index" : "hotel", "_type" : "_doc", "_id" : "006", "_score" : 1.0, "_source" : { "title" : "京盛集团精选酒店", "city" : "上海", "price" : "500.00", "create_time" : "2022-01-29 22:50:00", "full_room" : true, "location" : { "lat" : 40.918229, "lon" : 118.422011 }, "praise" : 20 } } ] } }

如果我需要查询大于500元(不包含边界值)的酒店:

GET /hotel/_search { "query": { "range": { "price": { "gt": "500" } } } }

注意,使用range查询时,查询值必须符合该字段在mappings中设置的规范。例如,在酒店索引中,price字段是double类型,则range应该使用数值型或者数值类型的字符串形式,不能使用其他形式。以下示例将导致ES返回错误:

GET /hotel/_search { "query": { "range": { "price": { "gt": "abc" } } } }

执行上述DSL后,ES返回信息如下:

{ "error" : { "root_cause" : [ { "type" : "query_shard_exception", "reason" : "failed to create query: For input string: \"abc\"", "index_uuid" : "az-MqIf9QM6asEIfivIBLQ", "index" : "hotel" } ], "type" : "search_phase_execution_exception", //range查询解析异常 "reason" : "all shards failed", "phase" : "query", "grouped" : true, "failed_shards" : [ { "shard" : 0, "index" : "hotel", "node" : "ER773I31Sx-wJuJwJCh7Ng", "reason" : { "type" : "query_shard_exception", //构建range查询时出现异常 "reason" : "failed to create query: For input string: \"abc\"", "index_uuid" : "az-MqIf9QM6asEIfivIBLQ", "index" : "hotel", "caused_by" : { //字符串类型不能转换为range查询对应的数值型数据 "type" : "number_format_exception", "reason" : "For input string: \"abc\"" } } } ] }, "status" : 400 }

和term查询类似,查询日期型的字段时,需要遵循该字段在mappings中定义的格式进行查询。例如create_time使用的格式为"yyyy-MM-dd HH:mm:ss",则range查询应该使用如下方式:

GET /hotel/_search { "query": { "range": { "create_time": { "gte": "2021-02-27 22:00:00", "lte": "2024-02-27 22:00:00" } } } }

在Java客户端上构建range请求是使用QueryBuilders.rangeQuery()方法实现的,该方法的参数为字段名称,然后再调用对应的方法即可构建相应的查询范围。可以调用gt()、lt()、gte()、lte()等方法分别实现大于、小于、大于等于、小于等于等查询范围。在使用时支持链式编程,可以连着使用"."操作符,这样不用拆分语句,也比较容易理解,一下通过range查询完成一个createTime大于等于输入的createTimeStart和小于等于createEnd的一个范围查询 Service层:

public List rangeQuery(HotelDocRequest hotelDocRequest) throws IOException { //新建搜索请求 String indexName = hotelDocRequest.getIndexName(); if (CharSequenceUtil.isBlank(indexName)) { throw new SearchException("索引名不能为空"); } SearchRequest searchRequest = new SearchRequest(indexName); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); Date createTimeStart = hotelDocRequest.getCreateTimeStart(); String createTimeStartToSearch = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createTimeStart); Date createTimeEnd = hotelDocRequest.getCreateTimeEnd(); String createTimeEndToSearch = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createTimeEnd); searchSourceBuilder.query(QueryBuilders.rangeQuery("create_time").gte(createTimeStartToSearch).lte(createTimeEndToSearch)); searchRequest.source(searchSourceBuilder); return getQueryResult(searchRequest); }

Controller层:

@PostMapping("/query/range") public FoundationResponse rangeQuery(@RequestBody HotelDocRequest hotelDocRequest) { try { List hotelList = esQueryService.rangeQuery(hotelDocRequest); if (CollUtil.isNotEmpty(hotelList)) { return FoundationResponse.success(hotelList); } else { return FoundationResponse.error(100,"no data"); } } catch (IOException e) { log.warn("搜索发生异常,原因为:{}", e.getMessage()); return FoundationResponse.error(100, e.getMessage()); } catch (Exception e) { log.error("服务发生异常,原因为:{}", e.getMessage()); return FoundationResponse.error(100, e.getMessage()); } }

postman调用该接口: 在这里插入图片描述

三、exists查询

在某些场景下,我们希望找到某个字段不为空的文档,则可以用exists搜索。字段不为空的条件有:

值存在且不是null值不是空数组值是数组,但不是[null]

为方便测试,给索引hotel增加tag字段,DSL如下:

POST /hotel/_mapping { "properties": { "tag":{ "type": "keyword" } } }

下面向该索引中分别写入3条字段为空的数据。 添加tag字段值为null的文档,DSL如下:

POST /hotel/_create/020 { "title":"环球酒店", "tag":null }

添加tag字段是空数组的文档,DSL如下:

POST /hotel/_create/021 { "title":"环球酒店2", "tag":[] }

添加tag为数组,其中只有一个元素,且该元素为null的文档,DSL如下:

POST /hotel/_create/022 { "title":"环球酒店3", "tag":[null] }

上面3种情况的数据使用exists查询都不命中,查询的DSL如下:

GET /hotel/_search { "query": { "exists": { "field": "tag" } } }

返回结果如下:

{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 0, //命中的文档个数为0 "relation" : "eq" }, "max_score" : null, "hits" : [ ] //命中的文档集合为空 } }

在java客户端中进行查询时,可以调用QueryBuilders.existsQuery(String name)方法新建一个exists查询,传递的name参数是目标字段名称。以下是使用Java客户端构建exists查询的示例: service层:

public List existQuery(HotelDocRequest hotelDocRequest) throws IOException { //新建搜索请求 String indexName = hotelDocRequest.getIndexName(); String propertiesName = hotelDocRequest.getPropertiesName(); if (CharSequenceUtil.isBlank(indexName)) { throw new SearchException("索引名不能为空"); } if (CharSequenceUtil.isBlank(propertiesName)) { throw new SearchException("想要查询的字段名不能为空"); } SearchRequest searchRequest = new SearchRequest(indexName); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.from(hotelDocRequest.getOffset()); searchSourceBuilder.size(hotelDocRequest.getLimit()); searchSourceBuilder.query(QueryBuilders.existsQuery(propertiesName)); searchRequest.source(searchSourceBuilder); return getQueryResult(searchRequest); }

controller层:

@PostMapping("/query/exist") public FoundationResponse existQuery(@RequestBody HotelDocRequest hotelDocRequest) { try { List hotelList = esQueryService.existQuery(hotelDocRequest); if (CollUtil.isNotEmpty(hotelList)) { return FoundationResponse.success(hotelList); } else { return FoundationResponse.error(100,"no data"); } } catch (IOException e) { log.warn("搜索发生异常,原因为:{}", e.getMessage()); return FoundationResponse.error(100, e.getMessage()); } catch (Exception e) { log.error("服务发生异常,原因为:{}", e.getMessage()); return FoundationResponse.error(100, e.getMessage()); } }

postman调用即可,比如搜索之前tag字段: 没有搜到,所以报了no data 在这里插入图片描述 如果搜索title,则会将title不为null的值全部搜索出来,由于title不为空的比较多,我这边只查前3条: 在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3